﻿/*  CamHelper (See http://camhelper.codeplex.com/ or https://bitbucket.org/AndiKS/camhelper for more Info)
    Copyright (C) 2011 Andreas Wimmer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using CamHelper.Interpreter;
using NUnit.Framework;

namespace CamHelper.Tests.Interpreter
{
    public class ParserTest
    {
        [Test]
        public void OneWord()
        {
            var cmd = "test";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var tree0 = tree[0] as Literal;
            Assert.AreNotEqual(tree0, null);
            Assert.AreEqual(tree0.Word, cmd);
        }

        [Test]
        public void TwoWord()
        {
            var cmd = "one two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            Assert.AreEqual((tree[0] as Literal).Word, "one");
            Assert.AreEqual((tree[1] as Literal).Word, " ");
            Assert.AreEqual((tree[2] as Literal).Word, "two");
        }

        #region Operationstypen

        [Test]
        public void Addition()
        {
            var cmd = "one+two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.Addition);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void AdditionWhiteSpace()
        {
            var cmd = "one + two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.Addition);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void Subtraction()
        {
            var cmd = "one-two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.Subtraction);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void Multiplication()
        {
            var cmd = "one*two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.Multiplication);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void Division()
        {
            var cmd = "one/two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.Division);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void Remainder()
        {
            var cmd = "one%two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.Remainder);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void LessThan()
        {
            var cmd = "one<two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.LessThan);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void GreaterThan()
        {
            var cmd = "one>two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.GreaterThan);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void LessThanOrEqual()
        {
            var cmd = "one<=two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.LessThanOrEqual);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void GreaterThanOrEqual()
        {
            var cmd = "one>=two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.GreaterThanOrEqual);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void Equal()
        {
            var cmd = "one==two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.Equal);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void NotEqual()
        {
            var cmd = "one!=two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.NotEqual);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void Assignment()
        {
            var cmd = "one=two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.Assignment);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void LogicalAnd()
        {
            var cmd = "one&two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.LogicalAnd);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void ConditionalAnd()
        {
            var cmd = "one&&two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.ConditionalAnd);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        [Test]
        public void ConditionalOr()
        {
            var cmd = "one||two";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var op = tree[0] as Operation;
            Assert.AreEqual(1, tree.Count);
            Assert.AreEqual(op.Type, OperationType.ConditionalOr);
            Assert.AreEqual((op.Left as Literal).Word, "one");
            Assert.AreEqual((op.Right as Literal).Word, "two");
        }

        #endregion

        [Test]
        public void Condition()
        {
            var cmd = "tool.rad==0.25?0.05:0";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            Assert.AreEqual(1, tree.Count);
        }

        [Test]
        public void Parentheses()
        {
            var cmd = "(1+2)/3";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            Assert.AreEqual(1, tree.Count);

            var op1 = tree[0] as Operation;
            Assert.AreNotEqual(null, op1);
            Assert.AreEqual(OperationType.Division, op1.Type);

            var par = op1.Left as Parentheses;
            Assert.AreNotEqual(null, par);

            var op2 = par.Content as Operation;
            Assert.AreNotEqual(null, op2);
            Assert.AreEqual(OperationType.Addition, op2.Type);
            Assert.AreEqual("1", (op2.Left as Literal).Word);
            Assert.AreEqual("2", (op2.Right as Literal).Word);

            var lit = op1.Right as Literal;
            Assert.AreNotEqual(null, lit);
            Assert.AreEqual("3", lit.Word);
        }

        [Test]
        public void Parentheses2()
        {
            var cmd = "((1+2)/3)";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            Assert.AreEqual(1, tree.Count);
        }

        [Test]
        public void Parentheses3()
        {
            var cmd = "((1+2)/3)+5";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            Assert.AreEqual(1, tree.Count);
        }

        [Test]
        public void PointBeforeLine()
        {
            var cmd = "1+2/3";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            Assert.AreEqual(1, tree.Count);
        }

        [Test]
        public void FunctionRound()
        {
            var cmd = "ROUND(1.234567,3)";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            Assert.AreEqual(1, tree.Count);
        }

        [Test]
        public void SearchParameter()
        {
            var cmd = "(tool.rad == 0) ? .global.path_type : 0";

            var parser = new Parser();
            var result = parser.SearchParameter(cmd);

            Assert.AreEqual(2, result.Count);
        }

        [Test]
        public void SearchParameter_Round()
        {
            var cmd = "ROUND(1.234567,3)";

            var parser = new Parser();
            var result = parser.SearchParameter(cmd);

            Assert.AreEqual(0, result.Count);
        }

        [Test]
        public void SearchParameter_round()
        {
            var cmd = "round(1.234567,3)";

            var parser = new Parser();
            var result = parser.SearchParameter(cmd);

            Assert.AreEqual(0, result.Count);
        }

        [Test]
        public void SearchParameterInput()
        {
            var cmd = "(tool.rad == 0) ? .global.path_type : 0";
            var input = new ParameterCollection();
            var pToolRad = new Parameter("Tool", "Rad", "24.88");
            input.Add(pToolRad);

            var parser = new Parser();
            var output = parser.SearchParameter(cmd, input);

            Assert.That(output.Count, Is.EqualTo(2));
            var p2 = output[pToolRad.UniqueKey];
            Assert.That(p2, Is.SameAs(pToolRad));
            Assert.That(p2.Value, Is.EqualTo(pToolRad.Value));
        }

        [Test]
        public void SearchParameterInputWithParameter()
        {
            var cmd = "(tool.rad == 0) ? .global.path_type : 0";
            var input = new ParameterCollection();
            input.Add(new Parameter("Tool", "Rad", "2"));
            input.Add(new Parameter("global", "path_type", "tool.diam*0.5"));

            var parser = new Parser();
            var output = parser.SearchParameter(cmd, input);

            Assert.That(output.Count, Is.EqualTo(3));
        }

        [Test]
        public void SearchParameterCategorieWithOutKey()
        {
            var cmd = ".global";

            var parser = new Parser();
            var result = parser.SearchParameter(cmd);

            Assert.AreEqual(1, result.Count);
        }

        [Test]
        public void SearchParameterCategorieWithOutKeyButWithPoint()
        {
            var cmd = ".global.";

            var parser = new Parser();
            var result = parser.SearchParameter(cmd);

            Assert.AreEqual(1, result.Count);
        }

        [Test]
        public void SearchParameterOneParameter()
        {
            var cmd = ".global.key";

            var parser = new Parser();
            var result = parser.SearchParameter(cmd);

            Assert.AreEqual(1, result.Count);
        }

        [Test]
        public void NegativNumber()
        {
            var cmd = "-5";

            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            Assert.AreEqual(1, tree.Count);
            Assert.That(tree[0], Is.InstanceOf(typeof(NegativNumber)));
        }

        [Test]
        public void NegativNumberAfterCondition()
        {
            var cmd = "1=1?-5:-8";

            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            var condition = tree[0] as Condition;

            Assert.That(condition.FirstTrue, Is.InstanceOf(typeof(NegativNumber)));
            Assert.That(condition.SecondFalse, Is.InstanceOf(typeof(NegativNumber)));
        }

        [Test]
        public void Empty()
        {
            var cmd = "";
            var tokens = Parser.StaticScan(cmd);
            var tree = Parser.StaticParse(tokens);

            Assert.That(tree.Count, Is.EqualTo(0));
        }
    }
}
